package com.tony.juc.StampedLock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * Created with IntelliJ IDEA.
 *
 * @Title: StampedLockDemo003
 * @Auther: 皮蛋布丁
 * @Date: 2022/04/09/21:51
 * @Description: 实现乐观锁，解决资源读写不平衡问题
 */
public class StampedLockDemo003 {

    public static void main(String[] args) {

        StampedLockAccountAccount003 account = new StampedLockAccountAccount003("皮蛋布丁", 0); // 定义银行账户信息
        int money[] = new int[]{110, 230, 10_000}; // 定义要存款的金额
        for (int x = 0; x < 100; x++) { // 10个读锁
            new Thread(() -> {
                while (true) {
                    System.out.print(account);
                }
            }, "取款线程 - " + x).start();
        }
        for (int x = 0; x < 2; x++) {  // 2个写锁
            new Thread(() -> {
                for (int y = 0; y < money.length; y++) {
                    account.save(money[y]); // 存款操作
                }
            }, "存款线程 - " + x).start();
        }
    }
}
class StampedLockAccountAccount003 {// 模拟银行账户
    private String name; // 账户的名称
    private int asset; // 账户的资金，资金使用整型
    private StampedLock stampedLock = new StampedLock(); // 标记戳锁

    public StampedLockAccountAccount003(String name, int asset) {
        this.name = name;
        this.asset = asset;
    }

    public void save(int asset) { // 向银行账户之中进行存款操作
        long stamp = this.stampedLock.writeLock(); // 获取写锁，这个时间戳用于解锁
        boolean flag = true; //循环控制
        try {
            long writeStamp = this.stampedLock.tryConvertToWriteLock(stamp); // 将读锁转为写锁
            while (flag) { //循环性的尝试转换
                if (writeStamp != 0) { //可以获取到读锁转换后的写锁
                    stamp =writeStamp; //修改标记戳
                    this.asset += asset; // 存款处理
                    TimeUnit.SECONDS.sleep(2); // 模拟业务的处理延迟
                    System.err.printf("【%s】修改银行资产，当前的资产为：%10.2f%n", Thread.currentThread().getName(), this.asset / 100.0);
                    flag = false; //退出当前循环
                } else {
                    this.stampedLock.unlockRead(stamp); //强制性的释放读锁
                    writeStamp = this.stampedLock.writeLock();//获取写锁
                    stamp = writeStamp; //修改标记戳
                }
            }
        } catch (Exception e) {
        } finally {
            this.stampedLock.unlockWrite(stamp); // 解除写锁
        }
    }

    @Override
    public String toString() {
        long stamp = this.stampedLock.tryOptimisticRead(); // 尝试获取乐观锁
        try {
            int current = this.asset; //获取当前的金额，这个金额可能过时
            TimeUnit.MILLISECONDS.sleep(200); // 模拟业务延迟
            if (!this.stampedLock.validate(stamp)) { //标记戳不正确
                stamp = this.stampedLock.readLock(); //重新创建一个读锁
                try {
                    current = this.asset; //因为有可能数据被修改
                } finally {
                    this.stampedLock.unlockRead(stamp); //释放读锁
                }
            }
            return String.format("〖%s〗账户名称：%s、账户余额：%10.2f %n", Thread.currentThread().getName(), this.name, current / 100.0);
        } catch (Exception e) {
            return null;
        }
    }
}
